1. Field of the Invention
The present invention relates generally to information processing environments and, more particularly, to an attribute-based component programming system and methodology for object-oriented programming languages.
2. Description of the Background Art
Before a digital computer may accomplish a desired task, it must receive an appropriate set of instructions. Executed by the computer's microprocessor, these instructions, collectively referred to as a “computer program”, direct the operation of the computer. Expectedly, the computer must understand the instructions which it receives before it may undertake the specified activity.
Owing to their digital nature, computers essentially only understand “machine code”, i.e., the low-level, minute instructions for performing specific tasks—the sequence of ones and zeros that are interpreted as specific instructions by the computer's microprocessor. Since machine language or machine code is the only language computers actually understand, all other programming languages represent ways of structuring human language so that humans can get computers to perform specific tasks.
While it is possible for humans to compose meaningful programs in machine code, practically all software development today employs one or more of the available programming languages. The most widely used programming languages are the “high-level” languages, such as C++, Pascal, or more recently Java and C#. These languages allow data structures and algorithms to be expressed in a style of writing that is easily read and understood by fellow programmers.
A program called a “compiler” translates these instructions into the requisite machine language. In the context of this translation, the program written in the high-level language is called the “source code” or source program. The ultimate output of the compiler is a compiled module such as a compiled C “object module”, which includes instructions for execution ultimately by a target processor, or a compiled Java class, which includes bytecodes for execution ultimately by a Java virtual machine. A Java compiler generates platform-neutral “bytecodes”—an architecturally neutral, intermediate format designed for deploying application code efficiently to multiple platforms.
During application development with object-oriented programming languages such as Java or C#, many times it would be beneficial to be able to program in a declarative style, that is to specify what needs to be achieved (declarative behavior) without specifying directly how it is to be achieved (implementation). Object-oriented programming generally promotes software re-use through inheritance or delegation, and the coding of inheritance or delegation to re-use existing implementation code can be seen as an expression of what needs to be achieved, but it is not declarative.
A suitable declarative approach can enhance the productivity of object-oriented programming. A declarative approach can help to avoid bugs that are due to incorrect placement of procedural programming constructs. It can simplify the process of re-use through inheritance, since inheritance sometimes requires the coding of a non-trivial interaction between the caller and the callee (superclass) objects. It can simplify the process of re-use through delegation, since delegation sometimes requires the coding of a non-trivial interaction between the caller and the callee (delegate) objects. Also, it can support substitutability of implementations (usually delegate classes) without the need to code callers using a factory design pattern. In addition, a declarative approach can support dynamic reconfiguration of applications without requiring changes to application code, including the possibility that new declarative behaviors can be added to existing applications.
Among the prior approaches that have been utilized for defining re-usable aspects of program behavior is aspect-oriented programming (AOP). With aspect-oriented programming external metadata or new language syntax is used to define re-usable aspects of program behavior, with “pointcuts” designating locations in existing code which are to be modified, and “advice” which specifies how to modify (i.e., alter, extend, or wrap) the designated code. Aspect-oriented programming is based on the idea that systems are better programmed by specifying the various concerns (properties or areas of interest) of a system and some description of their relationships and then relying on mechanisms in the underlying aspect-oriented environment to compose them together into a coherent program. Aspect-oriented software development makes it possible to modularize crosscutting aspects of a system. Like objects, aspects may arise at any stage of the software lifecycle, including requirements specification, design, implementation, and so forth. Common examples of crosscutting aspects are design or architectural constraints, systemic properties or behaviors (e.g., logging and error recovery), and features. For further information on aspect-oriented programming, see e.g., Bollert, K. “On Weaving Aspects”, in International Workshop on Aspect Oriented Programming at ECOOP99 (European Conference for Object Oriented Programming, June 1999), the disclosure of which is hereby incorporated by reference. An example of an aspect-oriented programming system is the AspectJ AOP programming system for Java™ available from the Eclipse Foundation. However, with the AspectJ programming system, source code must be available before aspects can be applied, and aspects cannot be applied or inspected at run-time. In addition, aspects can be somewhat difficult to understand and use.
Another technique is to use internal metadata with compiler support. Internal metadata (usually referred to as “attributes”) or new language syntax is included in the code. The compiler processes the attributes and generates additional code to implement the desired declarative behavior and/or save the attributes for later retrieval at run-time. Callers use direct class-level calls, as they would if the attributes had not been present. A well-known example of this is the support for “attributes” in the Microsoft .NET platform. Some system-defined attributes result in code generation. There is no explicit support for user-defined attributes that result in code generation, but user-defined attributes can be retrieved at run-time. User-defined declarative behavior must use explicit inheritance or delegation to call superclasses or delegates, which can then fetch attribute values and act upon them.
Metadata can be used with interfaces, a code generator, and a factory pattern. Metadata (sometimes referred to as a deployment descriptor) is defined externally to, or perhaps within, the application source code. A static code generator is run over this metadata to generate additional classes which implement the desired declarative behavior as well as delegating to the original application code. At run time, a factory design pattern is used to obtain instances of the interfaces, and callers use indirect (interface) calls. A well-known example of this is the Enterprise JavaBeans (EJB) architecture, which uses user-defined Java classes, interfaces, and XML deployment descriptors. Tools such as XDoclet can generate the interfaces and XML deployment descriptors from annotations (attributes) within the application source code (comments or static data values).
Metadata, combined with interfaces, dynamic proxies, and a factory pattern is another alternative technique. Metadata is defined externally to, or perhaps within, the application source code. At run time, a factory design pattern is used to obtain instances of the interfaces via dynamic proxies—instances of generic classes that can be precoded to implement arbitrary interfaces using run-time reflection and metadata, implementing the desired declarative behavior as well as delegating to the original application code. Callers use indirect (interface) calls. This technique is also used within some EJB/middleware implementations. It is generally less efficient than the use of a static code generator (dynamic, generic classes are often multi-purpose, with expensive code paths being executed to repeatedly check the metadata). However, this technique permits dynamic reconfiguration if the metadata is external to the application source code.
Metadata can also be used with interfaces, request interceptors, and a factory pattern. A static code generator is run over this metadata to generate additional classes which implement the desired declarative behavior by delegating to a configurable set of request interceptors, as well as delegating to the original application code. At run time, a factory design pattern is used to obtain instances of the interfaces, and callers use indirect (interface) calls. This technique is also used within some EJB/middleware implementations. It is generally less efficient than the sole use of a static code generator (interceptors sometimes have expensive code paths being executed to repeatedly check the metadata). As with dynamic proxies, this technique permits dynamic reconfiguration if the metadata is external to the application source code. In practice, it is not uncommon to find a combination of code generation, dynamic proxies, and request interceptors in use within a single application.
Alternatively, another technique is the use of metadata with load-time code modification. At run-time, when classes are being loaded, their instruction codes are modified by a mini-compiler (dynamic code generator) that has access to the metadata. The desired declarative behaviors are added at this time. After class loading, callers use direct class-level calls, as they would if the metadata had not been present. Some recent implementations of aspect-oriented programming use this technique. This technique permits dynamic reconfiguration if the metadata is external to the application source code. However, this approach has a number of drawbacks, including that code debugging is difficult due to lack of source code for the modified instruction codes generated by load-time code modification. Also, this approach constrains the types of code modifications to those that can be made by the mini-compiler and it also typically requires complex class loader implementations.
What is needed is an improved solution for developing programs in a declarative style that is relatively easy to understand and use and provides for flexibility in implementation. The solution should support user-defined attributes and both static and dynamic code generation. Ideally, the solution should also support dynamic program reconfiguration. The present invention provides a solution for these and other needs.